import puppeteer from "puppeteer";
import { PDFDocument } from 'pdf-lib';
import fs from 'fs';

const getSpecifiedIssues = async (periods: string, timeout = 100 * 1000) => {
    const url = `https://www.3dcax.com/duzhe/${periods}`;
    const browser = await puppeteer.launch({ headless: true, ignoreHTTPSErrors: true, timeout });
    const page = await browser.newPage();
    page.setDefaultNavigationTimeout(timeout);
    page.setDefaultTimeout(timeout);
    await page.goto(url);
    const { aList } = await page.evaluate(() => {
        const getByXpath = (xpath: string) => document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        const header = document.querySelector("header");
        header?.parentNode?.removeChild(header);

        const position = document.querySelector('.weizhi');
        position?.parentNode?.removeChild(position);

        const main = document.querySelector('main');
        if (main?.style) {
            main.style.marginTop = '0px';
            main.style.display = 'flex';
            main.style.flexDirection = 'column';
            main.style.alignItems = 'center';
        }
        //换页符
        const pageSeparator = document.createElement('div');
        pageSeparator.style.pageBreakBefore = 'always';
        main?.insertBefore(pageSeparator, main.firstChild);

        //封面
        const cover = getByXpath(`/html/body/main/aside/div[1]/i/a/img`) as HTMLElement;
        cover.style.width = '100%';
        cover.style.marginTop = '40px';
        cover && main?.insertBefore(cover, main.firstChild);


        const rightBox = document.querySelector('.rbox');
        rightBox?.parentNode?.removeChild(rightBox);
        const leftBox = document.querySelector<HTMLElement>('.lbox');
        if (leftBox?.style) {
            leftBox.style.float = 'none';
            leftBox.style.width = 'calc(100% - 110px)';
        }

        const footer = document.querySelector("footer");
        footer?.parentNode?.removeChild(footer);

        const aList: { title: string, href: string, articleBuffer?: Buffer }[] = [];
        Array.from((getByXpath(`/html/body/main/div[2]/div/ul`) as HTMLElement)?.children || []).forEach(element => {
            const computedStyles = getComputedStyle(element);
            const currentFontSize = parseFloat(computedStyles.fontSize);
            if (!isNaN(currentFontSize)) {
                const newFontSize = currentFontSize + 10;
                (element as HTMLElement).style.fontSize = `${newFontSize}px`;
            }
            const tagA = element.getElementsByTagName('a')[0];
            if (tagA) {
                aList.push({ title: tagA.innerHTML, href: tagA.href });
            }
        });
        console.log({ aList });
        return { aList }
    });
    const firstPartPath = `./pdf/${Date.now()}.pdf`;
    const firstPartBuffer = await page.pdf({ path: firstPartPath, format: 'A4', scale: 1, margin: { top: '40px', bottom: '40px' } });
    try {
        fs.unlinkSync(firstPartPath);
        console.log(`《读者》第${periods}期已爬取1/${1 + aList.length}`);
    } catch (err) {
        err ? console.log(`封面目录碎片删除失败`) : console.log(`封面目录碎片删除成功`)
    }
    const mergedDoc = await PDFDocument.create();
    const pdfDoc = await PDFDocument.load(firstPartBuffer);
    const copiedPages = await mergedDoc.copyPages(pdfDoc, pdfDoc.getPageIndices());
    copiedPages.forEach((page) => mergedDoc.addPage(page));
    let partNum = 0;
    for (const aItem of aList) {
        partNum++;
        const { href } = aItem;
        await page.goto(href);
        await page.evaluate(() => {
            const header = document.querySelector("header");
            header?.parentNode?.removeChild(header);

            const aside = document.querySelector("aside");
            aside?.parentNode?.removeChild(aside);

            const position = document.querySelector('.weizhi');
            position?.parentNode?.removeChild(position);

            const footer = document.querySelector("footer");
            footer?.parentNode?.removeChild(footer);

            const article = document.querySelector('article');
            const deleteClassNames = ['diggit', 'next_pre', 'wz_zhuanzai', 'tags', 'read_writer', 'read_love', 'ph_fenlei', 'ad', 'wz_tuijian', 'pinglun'];
            Array.from(article!.children).forEach(element => {
                if (deleteClassNames.includes(element.className)) {
                    element.parentNode?.removeChild(element);
                }
            });

            const main = document.querySelector('main');
            if (main?.style) {
                main.style.marginTop = '0px';
                main.style.display = 'flex';
                main.style.flexDirection = 'column';
                main.style.alignItems = 'center';
            }
            const leftBox = document.querySelector<HTMLElement>('.lbox');
            if (leftBox?.style) {
                leftBox.style.float = 'none';
                leftBox.style.width = 'calc(100% - 110px)';
            }

            const loop = (element: HTMLElement) => {
                Array.from(element.children, (child) => {
                    const computedStyles = getComputedStyle(child);
                    const currentFontSize = parseFloat(computedStyles.fontSize);
                    if (!isNaN(currentFontSize)) {
                        const newFontSize = currentFontSize + 10 > 24 ? 24 : currentFontSize + 10;
                        (child as HTMLElement).style.fontSize = `${newFontSize}px`;
                    }
                    if (child.children) loop(child as HTMLElement);
                });
            }
            loop(document.querySelector('article') as HTMLElement);
        });
        const articlePath = `./pdf/${Date.now()}.pdf`;
        const articleBuffer = await page.pdf({ path: articlePath, format: 'A4', scale: 1, margin: { top: '40px', bottom: '40px' } });
        try {
            fs.unlinkSync(articlePath);
            // process.stdout.write('\x1B[2J\x1B[0f');
            console.log(`《读者》第${periods}期已爬取${1 + partNum}/${1 + aList.length}`);
        } catch (err) {
            err ? console.log(`article碎片 ${articlePath}删除失败`) : console.log(`article碎片 ${articlePath}删除成功`)
        }
        aItem.articleBuffer = articleBuffer;
        const pdfDoc = await PDFDocument.load(articleBuffer);
        const copiedPages = await mergedDoc.copyPages(pdfDoc, pdfDoc.getPageIndices());
        copiedPages.forEach((page) => mergedDoc.addPage(page));
    }
    console.log(`已爬取数据，整合碎片中...`);
    // 保存合并后的 PDF
    const mergedBytes = await mergedDoc.save();
    fs.writeFileSync(`./pdf/《读者》第${periods}期.pdf`, mergedBytes);
    console.log(`碎片已整合，《读者》第${periods}期爬取完成`);
}

const main = async () => {
    const timeout = 100 * 1000;
    const browser = await puppeteer.launch({ headless: true, ignoreHTTPSErrors: true, timeout: timeout });
    const page = await browser.newPage();
    page.setDefaultNavigationTimeout(timeout);
    await page.goto(`https://www.3dcax.com/duzhe`);
    const { periods } = await page.evaluate(() => {
        const pList = document.querySelectorAll('.pics p');
        const pValueList = Array.from(pList, element => {
            const value = element.innerHTML;
            return value;
        });
        return {
            periods: pValueList.map(item => {
                const match = item.match(/(\d{4})年(\d{1,2})期/);
                const year = match?.[1];
                const month = match?.[2].padStart(2, '0'); // 补齐月份为两位数
                return `${year}${month}`;
            })
        }
    });
    const chunkedArray = [];
    const chunkSize = 5; // 每组包含的字符串数量
    for (let i = 0; i < periods.length; i += chunkSize) {
        const chunk = periods.slice(i, i + chunkSize);
        chunkedArray.push(chunk);
    }
    console.log({ chunkedArray });

    const loopChunk = async (chunk:string[]) => {
        return new Promise(async resolve => {
            let completeNum = 0;
            chunk.forEach(async period=>{
                await getSpecifiedIssues(period);
                completeNum++;
                if (completeNum === chunk.length) resolve(null);
            });
        })

    }
    for (const chunk of chunkedArray) {
        await loopChunk(chunk);
    }
    // await getSpecifiedIssues('202315');
}

main();